package com.bnd.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import com.bnd.common.ErrorCode;
import com.bnd.common.exception.BusinessException;
import com.bnd.config.AiConfig;
import com.bnd.service.ImageService;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.net.SocketTimeoutException;
import java.util.ArrayList;
import java.util.List;

/**
 * 文本生成图片服务实现类
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class ImageServiceImpl implements ImageService {

    private final AiConfig aiConfig;
    private final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * DashScope 文生图 REST 接口
     * 文档：https://help.aliyun.com/zh/dashscope/developer-reference/text-to-image
     */
    private static final String IMAGE_API = "https://dashscope.aliyuncs.com/api/v1/services/aigc/text2image/image-synthesis";
    private static final String TASK_API = "https://dashscope.aliyuncs.com/api/v1/tasks/";

    /**
     * 文本生成图片
     * @param prompt 提示词
     * @param size 图片尺寸
     * @param n 生成数量
     * @return 生成的图片URL列表
     */
    @Override
    public List<String> textToImage(String prompt, String size, Integer n) {
        // 参数校验
        if (StrUtil.isBlank(prompt)) {
            throw new BusinessException(ErrorCode.PARAMS_NULL, "提示词不能为空");
        }
        AiConfig.DashScope cfg = aiConfig.getDashscope();
        Assert.notNull(cfg, "DashScope配置未找到");
        
        // 功能开关仅作为快速熔断（false则直接抛出异常）
        if (cfg.getImageEnabled() != null && !cfg.getImageEnabled()) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "文生图功能已禁用");
        }
        
        String apiKey = cfg.getApiKey();
        if (StrUtil.isBlank(apiKey)) {
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "未配置DashScope API Key");
        }

        String model = StrUtil.blankToDefault(cfg.getImageModel(), "wanx-v1");
        String sizeToUse = StrUtil.blankToDefault(size, StrUtil.blankToDefault(cfg.getImageSize(), "1024*1024"));
        int num = (n == null || n < 1 || n > 4) ? 1 : n;

        // 构造请求体（异步）
        String body = StrUtil.format("{\n  \"model\": \"{}\",\n  \"input\": {\n    \"prompt\": \"{}\"\n  },\n  \"parameters\": {\n    \"n\": {},\n    \"size\": \"{}\",\n    \"async\": true\n  }\n}",
                model,
                escapeJson(prompt),
                num,
                sizeToUse);

        log.info("[Text2Image-Async] model={}, size={}, n={}, prompt={}", model, sizeToUse, num, StrUtil.cleanBlank(StrUtil.subPre(prompt, 200)));

        try {
            // 提交异步任务（关键：添加 X-DashScope-Async 头开启异步）
            HttpRequest request = HttpUtil.createPost(IMAGE_API)
                    .header("Content-Type", "application/json")
                    .header("Authorization", "Bearer " + apiKey)
                    .header("X-DashScope-Async", "enable")
                    .body(body)
                    .timeout(30_000);

            long start = System.currentTimeMillis();
            String taskId;
            try (HttpResponse resp = request.execute()) {
                long cost = System.currentTimeMillis() - start;
                int status = resp.getStatus();
                String respBody = resp.body();
                log.info("[Text2Image-Async] submit status={}, cost={}ms", status, cost);

                if (status < 200 || status >= 300) {
                    log.error("[Text2Image-Async] 提交失败, status={}, body={}", status, respBody);
                    throw new BusinessException(ErrorCode.OPERATION_ERROR, "文生图接口调用失败");
                }
                // 解析任务ID，兼容不同返回
                JsonNode root = objectMapper.readTree(respBody);
                String outTaskId = root.path("output").path("task_id").asText("");
                String reqId = root.path("request_id").asText("");
                taskId = StrUtil.isNotBlank(outTaskId) ? outTaskId : reqId;
                if (StrUtil.isBlank(taskId)) {
                    log.error("[Text2Image-Async] 未获取到任务ID, body={}", respBody);
                    throw new BusinessException(ErrorCode.OPERATION_ERROR, "未获取到任务ID");
                }
            }

            // 轮询任务结果（使用通用任务查询接口 /api/v1/tasks/{taskId}）
            String pollUrl = TASK_API + taskId;
            int maxTry = cfg.getImagePollMaxTry() == null ? 20 : Math.max(1, cfg.getImagePollMaxTry());
            int intervalMs = cfg.getImagePollIntervalMs() == null ? 3000 : Math.max(500, cfg.getImagePollIntervalMs());
            for (int i = 0; i < maxTry; i++) {
                try { Thread.sleep(intervalMs); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); }
                HttpRequest pollReq = HttpUtil.createGet(pollUrl)
                        .header("Authorization", "Bearer " + apiKey)
                        .timeout(20_000);
                try (HttpResponse pollResp = pollReq.execute()) {
                    int pollStatus = pollResp.getStatus();
                    String pollBody = pollResp.body();
                    log.info("[Text2Image-Async] poll try={}, status={}", i + 1, pollStatus);
                    if (pollStatus < 200 || pollStatus >= 300) {
                        log.warn("[Text2Image-Async] 轮询失败, status={}, body={}", pollStatus, pollBody);
                        continue;
                    }
                    JsonNode pollRoot = objectMapper.readTree(pollBody);
                    String statusStr = pollRoot.path("status").asText("");
                    if (StrUtil.isBlank(statusStr)) {
                        statusStr = pollRoot.path("output").path("task_status").asText("");
                    }
                    if ("SUCCEEDED".equalsIgnoreCase(statusStr) || "SUCCESS".equalsIgnoreCase(statusStr)) {
                        return parseImageUrls(pollBody);
                    } else if ("FAILED".equalsIgnoreCase(statusStr)) {
                        log.error("[Text2Image-Async] 任务失败, body={}", pollBody);
                        throw new BusinessException(ErrorCode.OPERATION_ERROR, "文生图任务失败");
                    }
                }
            }
            log.error("[Text2Image-Async] 轮询超时, taskId={}", taskId);
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "文生图任务超时");
        } catch (BusinessException e) {
            throw e;
        } catch (SocketTimeoutException e) {
            log.error("[Text2Image-Async] 请求超时", e);
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "文生图请求超时");
        } catch (Exception e) {
            log.error("[Text2Image-Async] 未知异常", e);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "文生图生成异常");
        }
    }

    /**
     * 解析返回中的图片URL列表
     * @param json 响应JSON字符串
     * @return 图片URL列表
     * @throws Exception 解析异常
     */
    private List<String> parseImageUrls(String json) throws Exception {
        JsonNode root = objectMapper.readTree(json);
        // 优先解析 output.images[*].url
        List<String> urls = new ArrayList<>();
        JsonNode output = root.path("output");
        if (output.isObject()) {
            JsonNode images = output.path("images");
            if (images.isArray()) {
                for (JsonNode img : images) {
                    String url = img.path("url").asText(null);
                    if (StrUtil.isNotBlank(url)) {
                        urls.add(url);
                    }
                }
            }

            // 兼容万相异步返回结构：output.results[*].url
            if (urls.isEmpty()) {
                JsonNode results = output.path("results");
                if (results.isArray()) {
                    for (JsonNode item : results) {
                        String url = item.path("url").asText(null);
                        if (StrUtil.isNotBlank(url)) {
                            urls.add(url);
                        }
                    }
                }
            }
        }
        if (CollUtil.isEmpty(urls)) {
            // 兼容 result.output[0].url 等其它结构
            JsonNode data = root.path("data");
            if (data.isArray()) {
                for (JsonNode item : data) {
                    String url = item.path("url").asText(null);
                    if (StrUtil.isNotBlank(url)) {
                        urls.add(url);
                    }
                }
            }
        }
        if (CollUtil.isEmpty(urls)) {
            log.warn("[Text2Image] 响应未包含图片URL: {}", json);
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "未获取到图片地址");
        }
        return urls;
    }

    /**
     * 对字符串进行简单的JSON转义
     * @param text 待转义的文本
     * @return 转义后的文本
     */
    private String escapeJson(String text) {
        if (text == null) {
            return "";
        }
        return text.replace("\\", "\\\\").replace("\"", "\\\"").replace("\n", "\\n");
    }
}


